﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Windows.Forms;

namespace MetaDemo.Tabs
{
    public partial class ObjectFields : UserControl
    {

        public static TabPage Get(Object o)
        {
            // Display information on the object.
            var fields = new ObjectFields();

            fields.grdFields.DataError += new DataGridViewDataErrorEventHandler(grdFields_DataError);
            fields.grdFields.CellContentClick += new DataGridViewCellEventHandler(grdFields_CellContentClick);
            fields.grdFields.AutoGenerateColumns = false;

            // Bind the fields to the columns
            var bs = new BindingSource();
            bs.DataSource = FieldInstance.GetInstances(o);
            fields.grdFields.DataSource = bs;

            // Create a tab page in which to display the information above.
            var page = GetTabPage();
            page.Controls.Add(fields);

            return page;
        }

        public static TabPage GetTabPage()
        {
            var page = new TabPage();

            page.Location = new System.Drawing.Point(4, 22);
            page.Name = "tabFields";
            page.Padding = new System.Windows.Forms.Padding(3);
            page.Size = new System.Drawing.Size(428, 391);
            page.Text = "Fields";
            page.UseVisualStyleBackColor = true;

            return page;
        }

        private ObjectFields()
        {
            InitializeComponent();
        }


        #region datagrid handler

        private static void grdFields_CellContentClick(object sender, DataGridViewCellEventArgs e)
        {
            if (0 > e.RowIndex)
            {
                return;
            }

            DataGridView view = sender as DataGridView;
            if (0 == e.ColumnIndex)
            {
                // Name link has been clicked: Forward navigation to the field value object!
                var fieldInstance = (FieldInstance)view.Rows[e.RowIndex].DataBoundItem;

                if (null == fieldInstance.Value)
                {
                    MessageBox.Show("Cannot display info for <null> object.");
                    return;
                }

                Forms.ObjectInfoForm.Get(fieldInstance.Value).Show();
            }
            if (2 == e.ColumnIndex)
            {
                // Type has been clicked: Forward navigation to the field's type object!
                var fieldInstance = (FieldInstance)view.Rows[e.RowIndex].DataBoundItem;
                Forms.ObjectInfoForm.Get(fieldInstance.Type).Show();
            }
        }

        private static void grdFields_DataError(object sender, DataGridViewDataErrorEventArgs args)
        {
            throw args.Exception;
        }

        #endregion

        private void grdFields_CellContentClick_1(object sender, DataGridViewCellEventArgs e)
        {

        }
    }



    public class FieldInstance
    {
        public static List<FieldInstance> GetInstances(Object obj)
        {
            List<FieldInstance> list = new List<FieldInstance>();

            // Fields
            foreach (FieldInfo fi in obj.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic))
            {
                list.Add(new FieldInstance(obj, fi));
            }

            return list;
        }

        
        private Object parent;
        private FieldInfo fieldInfo;

        private Type[] allowedFieldTypes = new Type[] { typeof(String), typeof(Int32), typeof (UInt64), typeof(Boolean) };

        public FieldInstance(Object p, FieldInfo fi)
        {
            parent = p;
            fieldInfo = fi;
        }

        public String FieldName { get { return fieldInfo.Name; } }

        public String FieldType { get { return fieldInfo.FieldType.ToString(); } }

        public String FieldValue
        {
            get
            {
                var v = Value;
                return (null == v) ? "null" : v.ToString();
            }
            set
            {
                try
                {
                    SetField(value);
                }
                catch (ReflectionException e) {
                    MessageBox.Show(e.Message);
                }
            }
        }

        public Type Type { get { return fieldInfo.FieldType; } }

        public Object Value { get { return fieldInfo.GetValue(parent); } }


        private void SetField(String value)
        {
            Type t = fieldInfo.FieldType;
            if (!allowedFieldTypes.Contains(t))
                throw new ReflectionException("Could not modify field of type " + fieldInfo.FieldType);

            if (typeof(String).Equals(t))
            {
                try
                {
                    fieldInfo.SetValue(parent, value);
                }
                catch (Exception)
                {
                    throw new ReflectionException("Cannot change field value.");
                }
            }
            else if (typeof(Int32).Equals(t))
            {
                Int32 i = 0;
                try
                {
                    i = Int32.Parse(value);
                }
                catch (Exception)
                {
                    throw new ReflectionException("Entered value cannot be converted to a valid 32-bit integer!");
                }
                try
                {
                    fieldInfo.SetValue(parent, i);
                }
                catch (Exception)
                {
                    throw new ReflectionException("Cannot change field value.");
                }
            }
            else if (typeof(UInt64).Equals(t))
            {
                UInt64 i = 0;
                try
                {
                    i = UInt64.Parse(value);
                }
                catch (Exception)
                {
                    throw new ReflectionException("Entered value cannot be converted to a valid 32-bit integer!");
                }
                try
                {
                    fieldInfo.SetValue(parent, i);
                }
                catch (Exception)
                {
                    throw new ReflectionException("Cannot change field value.");
                }
            }
            else if (typeof(Boolean).Equals(t))
            {
                Boolean b = true;
                try
                {
                    b = Boolean.Parse(value);
                }
                catch (Exception)
                {
                    throw new ReflectionException("Entered value cannot be converted to a valid boolean!");
                }
                try
                {
                    fieldInfo.SetValue(parent, b);
                }
                catch (Exception)
                {
                    throw new ReflectionException("Cannot change field value.");
                }
            }
            else
            {
                throw new ReflectionException("Cannot modify field.");
            }
        }
    }
}
